/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Engine2D.level;

import Engine2D.Game;
import Engine2D.graphics.particle.GLParticleSystem;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
import Engine2D.entity.Entity;
import Engine2D.entity.Player;
import Engine2D.graphics.GLSpriteSheet;
import Engine2D.input.InputHandler;
import Engine2D.level.tile.Tile;
import Engine2D.level.tile.TileManager;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

/**
 *
 * @author gary.morris
 */
public class Level {

    private Random random = new Random();
    /**
     * 
     */
    /**
     * 
     */
    public int w, h;
    private InputHandler input;
    /**
     * 
     */
    public byte[] tiles;
    /**
     * 
     */
    public byte[] data;
    /**
     * 
     */
    public List<Entity>[] entitiesInTiles;
    private int depth;
    /**
     * 
     */
    public List<Entity> entities = new ArrayList<Entity>();
    /**
     * 
     */
    public final int gemCount;
    private ArrayList<GLParticleSystem> particleSystems = new ArrayList<GLParticleSystem>();
    private Comparator<Entity> spriteSorter = new Comparator<Entity>() {

        public int compare(Entity e0, Entity e1) {
            if (e1.y < e0.y) {
                return +1;
            }
            if (e1.y > e0.y) {
                return -1;
            }
            return 0;
        }
    };

    /**
     * 
     * @param w
     * @param h
     * @param level
     * @param parentLevel
     * @param input
     */
    @SuppressWarnings("unchecked")
    public Level(int w, int h, int level, Level parentLevel, InputHandler input) {

        this.depth = level;
        this.input = input;
        this.w = w;
        this.h = h;
        byte[][] maps;



        maps = LevelGenerator.validNoiseMap(w, h);
        tiles = maps[0];
        data = maps[1];

        int[] count = new int[256];
        for (int i = 0; i < w * h; i++) {
            count[maps[0][i] & 0xff]++;
        }
        gemCount = count[TileManager.ore.id & 0xff];


        entitiesInTiles = new ArrayList[w * h];
        for (int i = 0; i < w * h; i++) {
            entitiesInTiles[i] = new ArrayList<Entity>();
        }


    }
    /**
     * 
     */
    public int test;

    /**
     * 
     * @param screen
     * @param xScroll
     * @param yScroll
     */
    public void renderBackground(Game screen, int xScroll, int yScroll) {

        /*
         * Binary right shift of 5 same as divide by 32
         */
        int xo = xScroll >> 5;
        int yo = yScroll >> 5;
        int w = (screen.WIDTH + 31) >> 5;
        int h = (screen.HEIGHT + 31) >> 5;

        GLSpriteSheet.getInstance().startBackground(32, 32, 1f);
        for (int y = yo; y <= h + yo; y++) {
            for (int x = xo; x <= w + xo; x++) {
                getTile(x, y).render(this, ((x * 32) - xScroll), ((y * 32) - yScroll), x, y);

            }
        }

        GLSpriteSheet.getInstance().stopBackground();
    }
    private List<Entity> rowSprites = new ArrayList<Entity>();
    /**
     * 
     */
    public Player player;

    /**
     * 
     * @param screen
     * @param xScroll
     * @param yScroll
     */
    public void renderSprites(Game screen, int xScroll, int yScroll) {
        int xo = xScroll >> 5;
        int yo = yScroll >> 5;
        int w = (screen.WIDTH + 31) >> 5;
        int h = (screen.HEIGHT + 31) >> 5;

        //screen.setOffset(xScroll, yScroll);
        for (int y = yo; y <= h + yo; y++) {
            for (int x = xo; x <= w + xo; x++) {
                if (x < 0 || y < 0 || x >= this.w || y >= this.h) {
                    continue;
                }
                //rowSprites.addAll(entitiesInTiles[x + y * this.w]);
                if (entitiesInTiles[x + y * this.w].size() > 0) {
                    for (int i = 0; i < entitiesInTiles[x + y * this.w].size(); i++) {
                        entitiesInTiles[x + y * this.w].get(i).render(xScroll, yScroll);
                    }
                }

            }
//            if (rowSprites.size() > 0) {
//
//                sortAndRender(rowSprites, xScroll, yScroll);
//            }
//
//            rowSprites.clear();
        }
        //screen.setOffset(0, 0);
    }

    private void sortAndRender(List<Entity> list, int xScroll, int yScroll) {
        Collections.sort(list, spriteSorter);
        for (int i = 0; i < list.size(); i++) {
            list.get(i).render(xScroll, yScroll);
        }
    }

    /**
     * 
     * @param x
     * @param y
     * @return
     */
    public Tile getTile(int x, int y) {
        if (x < 0 || y < 0 || x >= w || y >= h) {
            return TileManager.bedRock;
        }
        return TileManager.tiles[tiles[x + y * w]];
    }

    /**
     * 
     * @param x
     * @param y
     * @param t
     * @param dataVal
     */
    public void setTile(int x, int y, Tile t, int dataVal) {
        if (x < 0 || y < 0 || x >= w || y >= h) {
            return;
        }
        tiles[x + y * w] = t.id;
        data[x + y * w] = (byte) dataVal;
    }

    /**
     * 
     * @param x
     * @param y
     * @return
     */
    public int getData(int x, int y) {
        if (x < 0 || y < 0 || x >= w || y >= h) {
            return 0;
        }
        return data[x + y * w] & 0xff;
    }

    /**
     * 
     * @param x
     * @param y
     * @param val
     */
    public void setData(int x, int y, int val) {
        if (x < 0 || y < 0 || x >= w || y >= h) {
            return;
        }
        data[x + y * w] = (byte) val;
    }

    /**
     * 
     * @param entity
     */
    public void add(Entity entity) {
        if (entity instanceof Player) {
            player = (Player) entity;
        }



        entities.add(entity);
        entity.init(this);

        insertEntity(entity.x >> 5, entity.y >> 5, entity);
    }

    private void removeEntity(int x, int y, Entity e) {
        if (x < 0 || y < 0 || x >= w || y >= h) {
            return;
        }

        entitiesInTiles[x + y * w].remove(e);
    }

    private void insertEntity(int x, int y, Entity e) {
        if (x < 0 || y < 0 || x >= w || y >= h) {
            return;
        }

        entitiesInTiles[x + y * w].add(e);
    }

    /**
     * 
     * @param ticks
     */
    public void tick(int ticks) {

        for (int i = 0; i < w * h / 50; i++) {
            int xt = random.nextInt(w);
            int yt = random.nextInt(w);
            getTile(xt, yt).tick(this, xt, yt);
        }

        for (int i = 0; i < particleSystems.size(); i++) {
            if (!particleSystems.get(i).IsDead()) {

                particleSystems.get(i).tick();
            } else {
                particleSystems.remove(i);
            }
        }


        for (int i = 0; i < entities.size(); i++) {
            Entity e = entities.get(i);

            int xto = (e.x + 16) >> 5;
            int yto = (e.y + 16) >> 5;


            e.tick(ticks);

            if (e.removed) {
                entities.remove(i--);
                removeEntity(e.x >> 5, e.y >> 5, e);
            } else {

                int xtn = (e.x + 16) >> 5;
                int ytn = (e.y + 16) >> 5;

                if (xto != xtn || yto != ytn) {
                    removeEntity(xto, yto, e);
                    insertEntity(xtn, ytn, e);
                }
            }
        }
    }

    /**
     * 
     * @param particleSystem
     */
    public void addParticleSystem(GLParticleSystem particleSystem) {
        particleSystem.init();
        particleSystems.add(particleSystem);
    }

    /**
     * 
     * @param xScroll
     * @param yScroll
     */
    public void renderParticles(int xScroll, int yScroll) {

        if (particleSystems.size() > 0) {
            for (int i = 0; i < particleSystems.size(); i++) {

                if (!particleSystems.get(i).IsDead()) {
                    particleSystems.get(i).render(xScroll, yScroll);
                }

            }
        }
    }

    public void saveToDisk() throws FileNotFoundException, IOException {
        // Write to disk with FileOutputStream
        FileOutputStream f_out = new FileOutputStream("levelTiles.data");

        // Write object with ObjectOutputStream
        ObjectOutputStream obj_out = new ObjectOutputStream(f_out);

        // Write object out to disk
        obj_out.writeObject(tiles);
    }

    public void loadFromDisk() throws FileNotFoundException, IOException, ClassNotFoundException {
        // Read from disk using FileInputStream
        FileInputStream f_in = new FileInputStream("levelTiles.data");

        // Read object using ObjectInputStream
        ObjectInputStream obj_in =
                new ObjectInputStream(f_in);

        // Read an object
        Object obj = obj_in.readObject();

        if (obj instanceof byte[]) {
            // Cast object to a Vector
            tiles = (byte[]) obj;

            // Do something with vector....
        }
    }

    public void doubleSize() {
        byte[] one = tiles.clone();
        byte[] two = tiles.clone();
        tiles = new byte[one.length + two.length];

        for (int i = 0; i < tiles.length; ++i) {
            tiles[i] = i < one.length ? one[i] : two[i - one.length];
        }
        
        h = h * 2;
        
       ArrayList hello = new ArrayList<Entity>();
        entitiesInTiles = new ArrayList[w * h];
        for (int i = 0; i < w * h; i++) {
            entitiesInTiles[i] = hello;
        }
    }
}
